package com.library.xtype;

import java.lang.ref.WeakReference;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import static com.library.xtype.GlobalNuwaPool.getItemPropertys;

/**
 * 在此写用途
 *
 * @author: zhiwei
 * @date: 2017-09-14 16:20
 * @version: 9.1.0
 */
public class NuwaGroupRecyclerViewAdapter extends RecyclerView.Adapter<WrapRecyclerViewHolder> implements
    INuwaLifecycle, IAdapter {
    /**
     * 当前adapter VO <-> {@link INuwaItemBinder} 的映射关系
     */
    NuwaItemPool mNuwaPoolDalegate;
    /**
     * 当前 界面显示的ViewHlder 缓存
     */
    SparseArray<WeakReference<WrapRecyclerViewHolder>> mNuwaViewCache = new SparseArray<>(20);
    LayoutInflater mInflater;
    /**
     * 透传的点击事件，由Adapter使用者透传给{@link INuwaItemBinder}
     */
    View.OnClickListener mOnClickListener;

    /**
     * 存储实际的数据，可能是VO，或者是继承{@link INuwaGroupVO}的对象
     */
    List mNuwaGroupVOList = new ArrayList();

    /**
     * 记录feed 流中的数据。
     *
     * 模拟数据，为某个VO or {@link INuwaGroupVO} 在{@link #mNuwaGroupVOList} 中的位置
     */
    List<Integer> mFeedVOProxyList = new ArrayList<Integer>();
    /**
     * 某个ItemGroup在{@link #mFeedVOProxyList} 中的开始位置
     */
    List<Integer> mStartPositionInFeedVOProxyListForNuwaGroupVOList = new ArrayList<>();

    public NuwaGroupRecyclerViewAdapter() {
        this(new NuwaItemPool());
    }

    public NuwaGroupRecyclerViewAdapter(NuwaItemPool manager) {
        mNuwaPoolDalegate = manager;
    }

    public NuwaGroupRecyclerViewAdapter setNuwaPoolDalegate(NuwaItemPool nuwaPoolDalegate) {
        if (mNuwaPoolDalegate != null) {
            Collection<ItemProperty> itemProperties = mNuwaPoolDalegate.getItemPropertys();
            for (ItemProperty property : itemProperties) {
                register(property.getItemBinderClass());
            }
        }
        mNuwaPoolDalegate = nuwaPoolDalegate;
        return this;
    }

    public NuwaGroupRecyclerViewAdapter setOnClickListener(View.OnClickListener onClickListener) {
        mOnClickListener = onClickListener;
        return this;
    }

    /**
     * 方法将废弃，请使用
     *
     * @param voClazz vo
     * @param binder  binder extender INuwaItemBinder
     * @param <T>
     * @see #register(Class <T>)
     */
    public <T extends INuwaItemBinder> NuwaGroupRecyclerViewAdapter register(@NonNull Class voClazz,
                                                                             @NonNull Class<T> binder) {
        mNuwaPoolDalegate.register(voClazz, binder);
        return this;
    }

    /**
     * 此方法会自动查找 INuwaItemBinder 类中对应使用的VO。
     *
     * 注意使用此方法，Nuwa中一个VO只能对应一个Binder。
     *
     * @param binder binder
     * @param <T>
     */
    public <T extends INuwaItemBinder> NuwaGroupRecyclerViewAdapter register(@NonNull Class<T> binder) {
        Type genType = binder.getGenericSuperclass();
        Type[] params = ((ParameterizedType)genType).getActualTypeArguments();
        Class voClazz = (Class)params[0];
        mNuwaPoolDalegate.register(voClazz, binder);
        return this;
    }

    /**
     * 应用全局的NuwaItemPool
     */
    public NuwaGroupRecyclerViewAdapter applyGlobalMultiTypePool() {
        Collection<ItemProperty> itemProperties = getItemPropertys();
        for (ItemProperty property : itemProperties) {
            register(property.getItemVOClass(), property.getItemBinderClass());
        }
        return this;
    }

    @Override
    public void onViewRecycled(WrapRecyclerViewHolder holder) {
        super.onViewRecycled(holder);
        holder.onDestroy();
    }

    @Override
    public WrapRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        INuwaItemBinder nuwaItemView = mNuwaPoolDalegate.createItemView(parent.getContext(), viewType);
        // create nullable item view
        if (nuwaItemView == null) {
            nuwaItemView = new NullableIBinder();
        }
        if (mInflater == null) {
            mInflater = LayoutInflater.from(parent.getContext());
        }
        View contentView = nuwaItemView.onCreateView(null, parent, mInflater);
        WrapRecyclerViewHolder nuwaViewHolder = new WrapRecyclerViewHolder(contentView, nuwaItemView);
        mNuwaViewCache.put(nuwaViewHolder.hashCode(), new WeakReference<WrapRecyclerViewHolder>(nuwaViewHolder));
        if (mOnClickListener != null) {
            nuwaItemView.setOnClickListener(mOnClickListener);
        }
        return nuwaViewHolder;
    }

    @Override
    public void onBindViewHolder(WrapRecyclerViewHolder holder, int feedPosition) {
        int positionInVOGroup = getVOPosition(feedPosition);
        Object object = mNuwaGroupVOList.get(positionInVOGroup);
        if (object instanceof INuwaGroupVO) {
            BinderGroup binderGroup = getBinderGroup(feedPosition);
            holder.onBindView(feedPosition, binderGroup);
        } else {
            holder.onBindView(feedPosition, object);
        }
    }

    @Override
    public int getItemViewType(int feedPosition) {
        return mNuwaPoolDalegate.getItemViewType(getItem(feedPosition));
    }

    @Override
    public int getItemCount() {
        return mFeedVOProxyList.size();
    }

    private void lifeAction(int type) {
        for (int i = 0, nsize = mNuwaViewCache.size(); i < nsize; i++) {
            WeakReference<WrapRecyclerViewHolder> weakView = mNuwaViewCache.valueAt(i);
            WrapRecyclerViewHolder binder = weakView.get();
            if (binder != null) {
                switch (type) {
                    case 0:
                        binder.onCreate();
                        break;
                    case 1:
                        binder.onStart();
                        break;
                    case 2:
                        binder.onResume();
                        break;
                    case 3:
                        binder.onPause();
                        break;
                    case 4:
                        binder.onStart();
                        break;
                    case 5:
                        binder.onDestroy();
                        break;
                    default:
                        break;
                }

            }
        }
    }

    @Override
    public void onCreate() {
        lifeAction(0);
    }

    @Override
    public void onStart() {
        lifeAction(1);
    }

    @Override
    public void onResume() {
        lifeAction(2);
    }

    @Override
    public void onPause() {
        lifeAction(3);
    }

    @Override
    public void onStop() {
        lifeAction(4);
    }

    @Override
    public void onDestroy() {
        lifeAction(5);
        mNuwaViewCache.clear();
        mNuwaGroupVOList.clear();
        mFeedVOProxyList.clear();
        mStartPositionInFeedVOProxyListForNuwaGroupVOList.clear();
        mNuwaPoolDalegate.destory();

    }

    @Override
    public void clearData() {
        if (mFeedVOProxyList != null) {
            mNuwaGroupVOList.clear();
            mFeedVOProxyList.clear();
            mStartPositionInFeedVOProxyListForNuwaGroupVOList.clear();
            try {
                //Cannot call this method while RecyclerView is computing a layout or scrolling
                notifyDataSetChanged();
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    @UiThread
    @Override
    public void flushData(List voList) {
        if (mFeedVOProxyList != null) {
            mFeedVOProxyList.clear();
            mNuwaGroupVOList.clear();
            mStartPositionInFeedVOProxyListForNuwaGroupVOList.clear();
            notifyDataSetChanged();
        }
        if (voList != null) {
            appendData(0, voList);
        }
    }

    @UiThread
    @Override
    public void appendData(List data) {
        if (data == null && data.isEmpty()) {
            return;
        }
        if (mFeedVOProxyList == null && mFeedVOProxyList.isEmpty()) {
            flushData(data);
        } else if (data != null) {
            appendData(mFeedVOProxyList.size(), data);
        }
    }

    /**
     * @param feedPosition 插入位置
     * @param data 任意VO或者{@link INuwaGroupVO}对象
     */
    public void insertData(int feedPosition, Object data) {
        ArrayList list = new ArrayList(1);
        list.add(data);
        appendData(feedPosition, list);
    }

    /**
     *
     * @param feedPosition 插入位置
     * @param data 插入的数据,item可以是任意VO或者{@link INuwaGroupVO}对象
     */
    public void insertData(int feedPosition, List data) {
        appendData(feedPosition, data);
    }

    private void appendData(int startInsert, List data) {
        int size = data.size();
        int count = 0;
        boolean isNotifyAll = false;
        if (startInsert >= mFeedVOProxyList.size()) {
            startInsert = mFeedVOProxyList.size();
            isNotifyAll = true;
        }
        int lastInsertPosition = startInsert;
        for (int i = 0; i < size; i++) {
            count = add(lastInsertPosition, data.get(i), false);
            lastInsertPosition = lastInsertPosition + count;
        }
        try {
            //Cannot call this method while RecyclerView is computing a layout or scrolling
            if (isNotifyAll) {
                notifyDataSetChanged();
            }else {
                notifyItemRangeInserted(startInsert, lastInsertPosition - startInsert);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * @param nuwaGroupVOPosition VO or {@link INuwaGroupVO} 在{@link #mNuwaGroupVOList} 列表中的下标。
     * @return
     */
    private int getStartPositionInFeedList(int nuwaGroupVOPosition) {
        if (mStartPositionInFeedVOProxyListForNuwaGroupVOList.isEmpty()) {
            return 0;
        } else {
            if (nuwaGroupVOPosition < mStartPositionInFeedVOProxyListForNuwaGroupVOList.size()) {
                return mStartPositionInFeedVOProxyListForNuwaGroupVOList.get(nuwaGroupVOPosition);
            } else {
                return mFeedVOProxyList.size();
            }
        }
    }

    /**
     * 添加数据
     *
     * @param item 数据
     */
    public int add(Object item) {
        return add(mFeedVOProxyList.size(), item);
    }

    /**
     * 添加数据
     *
     * @param feedPosition 添加到Feed流中的位置
     * @param item         数据
     */
    public int add(final int feedPosition, Object item) {
       return add(feedPosition, item, true);
    }

    @UiThread
    private int add(final int feedPosition, Object item, boolean notify) {
        long time = System.currentTimeMillis();
        if (item == null) {
            return 0;
        }
        //mVOGroupList列表的位置
        int voPosition = getVOPosition(feedPosition);
        int voFeedStartPosition = getStartPositionInFeedList(voPosition);
        mNuwaGroupVOList.add(voPosition, item);

        //设置当前item在Feed中的数据
        List voList = getVoList(item);
        final List<Integer> feedPositions = new ArrayList<>();
        int itemSize = voList.size();
        for (int i = 0; i < itemSize; i++) {
            feedPositions.add(voPosition);
        }
        mFeedVOProxyList.addAll(voFeedStartPosition, feedPositions);
        //移动后续的位置
        for (int i = voFeedStartPosition + itemSize; i < mFeedVOProxyList.size(); i++) {
            mFeedVOProxyList.set(i, mFeedVOProxyList.get(i) + 1);
        }

        mStartPositionInFeedVOProxyListForNuwaGroupVOList.add(voPosition, voFeedStartPosition);
        for (int i = voPosition + 1; i < mStartPositionInFeedVOProxyListForNuwaGroupVOList.size(); i++) {
            mStartPositionInFeedVOProxyListForNuwaGroupVOList.set(i,
                mStartPositionInFeedVOProxyListForNuwaGroupVOList.get(i) + itemSize);
        }

        if (notify) {
            notifyItemRangeInserted(voFeedStartPosition, itemSize);
        }

        long time1 = System.currentTimeMillis();
        Log.e("致伟", this.getClass().getSimpleName() + " add: (Add time)=" + (time1 - time));
        return itemSize;
    }

    private List getVoList(Object item) {
        if (item instanceof INuwaGroupVO) {
            return ((INuwaGroupVO)item).getNuwaGroupVO();
        } else {
            List list = new ArrayList(1);
            list.add(item);
            return list;
        }
    }

    private int getVOPosition(int feedPosition) {
        if (feedPosition >= 0 && !mFeedVOProxyList.isEmpty()) {
            if (feedPosition < mFeedVOProxyList.size()) {
                return mFeedVOProxyList.get(feedPosition);
            } else {
                return mNuwaGroupVOList.size();
            }
        } else {
            return 0;
        }
    }

    /**
     * 获取VO在Feed中的位置
     *
     * @param object
     * @return
     */
    public int getFeedPosition(Object object) {
        int feedPosition = findFeedPositionByVO(object);
        return feedPosition;
    }

    int findFeedPositionByVO(Object feedVO) {
        int feedPosition = -1;
        for (int i = 0; i < mFeedVOProxyList.size(); i++) {
            Object object = getItem(i);
            if (object.equals(feedVO)) {
                feedPosition = i;
                break;
            }
        }
        return feedPosition;
    }

    /**
     * VO
     *
     * @param feedPosition feed 流中的数据
     * @return
     */
    @Override
    public Object getItem(int feedPosition) {
        BinderGroup binderGroup = getBinderGroup(feedPosition);
        return binderGroup.mFeedVO;
    }

    public
    @Nullable
    BinderGroup getBinderGroup(Object feedVO) {
        int feedPosition = findFeedPositionByVO(feedVO);
        if (feedPosition == -1) {
            return null;
        }
        return getBinderGroup(feedPosition);
    }

    /**
     * 返回 feedPosition 所在的组的数据
     *
     * @param feedPosition position
     * @return
     */
    public BinderGroup getBinderGroup(int feedPosition) {
        int positionInVOGroup = getVOPosition(feedPosition);
        int startInFeedList = getStartPositionInFeedList(positionInVOGroup);
        Object groupItem = mNuwaGroupVOList.get(positionInVOGroup);
        Object feedVO = null;
        if (groupItem instanceof INuwaGroupVO) {
            INuwaGroupVO nuwaItemGroupVO = (INuwaGroupVO)groupItem;
            feedVO = nuwaItemGroupVO.getNuwaGroupVO().get(feedPosition - startInFeedList);
        } else {
            feedVO = groupItem;
        }

        BinderGroup binderGroup = new BinderGroup(this, feedVO, positionInVOGroup, startInFeedList, feedPosition,
            groupItem,
            feedPosition - startInFeedList);
        return binderGroup;
    }

    @Override
    public int getRealCount() {
        return getItemCount();
    }

    private void offsetStartPosition(int startIndex, int offset) {
        for (int i = startIndex; i < mStartPositionInFeedVOProxyListForNuwaGroupVOList.size(); i++) {
            mStartPositionInFeedVOProxyListForNuwaGroupVOList.set(i,
                mStartPositionInFeedVOProxyListForNuwaGroupVOList.get(i) - offset);
        }
    }

    /**
     * 删除第一个找到的某一类型的数据，或级联的组。
     * @param clazz
     * @return
     */
    public int removeGroup(Class clazz) {
        Object feedVo = findVOByClassFirstToEnd(clazz);
        if (feedVo == null) {
            return -1;
        }
        return removeGroup(feedVo);
    }

    /**
     * 级联删除组
     *
     * @param feedVO data Object
     */
    public int removeGroup(Object feedVO) {
        int feedsPosition = findFeedPositionByVO(feedVO);
        if (feedsPosition == -1) {
            return -1;
        }
        return removeGroup(feedsPosition);
    }

    /**
     * 级联删除feedPosition所在组
     *
     * @param feedPosition 在列表中的位置
     */
    public int removeGroup(int feedPosition) {
       return removeGroup(feedPosition, true);
    }

    ///**
    // * 批量删除，若feedPosition属于木某个{@link INuwaGroupVO}，则级联删除。
    // * @param startFeedPosition 开始删除的位置
    // * @param feedCount 删除feed总数
    // */
    //public int removeGroup(int startFeedPosition, int feedCount) {
    //    int count = -1;
    //    if (startFeedPosition >= mFeedVOProxyList.size()) {
    //        return count;
    //    }
    //
    //
    //
    //    return count;
    //}

    /**
     * 级联删除feedPosition所在组
     *
     * @param feedPosition 在列表中的位置
     * @param notify       true 刷新界面
     */
    private int removeGroup(int feedPosition, boolean notify) {
        BinderGroup binderGroup = getBinderGroup(feedPosition);
        int startPositionInFeedList = getStartPositionInFeedList(binderGroup.mPositionInGroupList);
        int positionInVOGroup = binderGroup.mPositionInGroupList;
        int feedSize = 0;

        if (binderGroup.mNuwaGroupVO instanceof INuwaGroupVO) {
            INuwaGroupVO nuwaItemGroupVO = (INuwaGroupVO)binderGroup.mNuwaGroupVO;
            feedSize = nuwaItemGroupVO.getNuwaGroupVO().size();

            mNuwaGroupVOList.remove(positionInVOGroup);
            //级联删除索引
            for (int i = 0; i < feedSize; i++) {
                mFeedVOProxyList.remove(startPositionInFeedList);
            }
            //偏移索引位置
            for (int i = startPositionInFeedList; i < mFeedVOProxyList.size(); i++) {
                mFeedVOProxyList.set(i, mFeedVOProxyList.get(i) - 1);
            }
            mStartPositionInFeedVOProxyListForNuwaGroupVOList.remove(positionInVOGroup);
            offsetStartPosition(positionInVOGroup, feedSize);
            if (notify) {
                notifyItemRangeRemoved(startPositionInFeedList, feedSize);
            }
        } else {
            feedSize = 1;
            mNuwaGroupVOList.remove(binderGroup.mNuwaGroupVO);
            mFeedVOProxyList.remove(feedPosition);
            mStartPositionInFeedVOProxyListForNuwaGroupVOList.remove(positionInVOGroup);
            offsetStartPosition(positionInVOGroup, feedSize);
            if (notify) {
                notifyItemRangeRemoved(feedPosition, feedSize);
            }
        }
        return feedSize;
    }

    /**
     * 删除 某个VO。当vo可能属于某个组时，只单独删除这个VO
     *
     * @param feedVO data Object
     */
    @Override
    public void removeData(Object feedVO) {
        int feedsPosition = findFeedPositionByVO(feedVO);
        if (feedsPosition == -1) {
            return;
        }
        removeData(feedsPosition);
    }

    /**
     * @param feedsPosition in feedList position
     */
    public void removeData(int feedsPosition) {
        BinderGroup binderGroup = getBinderGroup(feedsPosition);
        if (binderGroup.mNuwaGroupVO instanceof INuwaGroupVO) {
            int starPosition = getStartPositionInFeedList(binderGroup.mPositionInGroupList);
            INuwaGroupVO nuwaItemGroupVO = (INuwaGroupVO)binderGroup.mNuwaGroupVO;
            //只有一条数据时，做级联删除
            if (nuwaItemGroupVO.getNuwaGroupVO().size() == 1) {
                removeGroup(feedsPosition);
            } else {
                mFeedVOProxyList.remove(feedsPosition);
                nuwaItemGroupVO.getNuwaGroupVO().remove(feedsPosition - starPosition);
                offsetStartPosition(binderGroup.mPositionInGroupList + 1, 1);
            }
        } else {
            int positionInVOGroup = binderGroup.mPositionInGroupList;
            mNuwaGroupVOList.remove(binderGroup.mNuwaGroupVO);
            mFeedVOProxyList.remove(feedsPosition);
            mStartPositionInFeedVOProxyListForNuwaGroupVOList.remove(positionInVOGroup);
            offsetStartPosition(positionInVOGroup, 1);
        }
        notifyItemRangeRemoved(feedsPosition, 1);
    }

    public Object findVOByClassEndToFirst(Class clazz) {
        Object feedVo = null;
        int size = mNuwaGroupVOList.size();
        for (int i = size - 1; i >= 0; i++) {
            List list = getVoList(mNuwaGroupVOList.get(i));
            for (Object vo : list) {
                if (vo.getClass().equals(clazz)) {
                    feedVo = vo;
                    return feedVo;
                }
            }
        }
        return feedVo;
    }

    public Object findVOByClassFirstToEnd(Class clazz) {
        Object feedVo = null;

        for (Object object : mNuwaGroupVOList) {
            List list = getVoList(object);
            for (Object vo : list) {
                if (vo.getClass().equals(clazz)) {
                    feedVo = vo;
                    return feedVo;
                }
            }
        }
        return feedVo;
    }

    /**
     * 返回所有的实际数据
     *
     * @return
     */
    @Override
    public List<Integer> getAllData() {
        return mFeedVOProxyList;
    }

    public static class BinderGroup<T> {
        public NuwaGroupRecyclerViewAdapter mAdapter;
        /**
         * 当前itemVO
         */
        public final T mFeedVO;
        /**
         * VO or {@link INuwaGroupVO}
         */
        public final Object mNuwaGroupVO;

        /**
         * 在{@link NuwaGroupRecyclerViewAdapter#mNuwaGroupVOList}中的位置。
         *
         * 当NuwaGroupRecyclerViewAdapter 调用多次remove、add方法后，此值需要调用{@link #update()}方法更新
         */
        public int mPositionInGroupList;
        /**
         * mFeedVO 在{@link NuwaGroupRecyclerViewAdapter#mFeedVOProxyList}中的位置
         *
         * 当NuwaGroupRecyclerViewAdapter 调用多次remove、add方法后，此值需要调用{@link #update()}方法更新
         */
        public int mPositionInFeeds;

        /**
         * {@link #mNuwaGroupVO} 在{@link NuwaGroupRecyclerViewAdapter#mFeedVOProxyList}中的开始位置
         * 当NuwaGroupRecyclerViewAdapter 调用多次remove、add方法后，此值需要调用{@link #update()}方法更新
         */
        public int mStartPositionInFeedList;

        /**
         * 若{@link #mNuwaGroupVO} 属于{@link INuwaGroupVO} 类型。则代表{@link #mFeedVO}在 {@link #mNuwaGroupVO}中的位置；
         * 否则 为0
         *
         * 当NuwaGroupRecyclerViewAdapter 调用多次remove、add方法后，此值需要调用{@link #update()}方法更新
         */
        public int mNuwaItemGroupIndex;

        /**
         * @param dataVO             当前itemVO
         * @param positionInVOGroup  在group中的位置
         * @param startInFeedList    {@link #mNuwaGroupVO} 在feed中的开始位置
         * @param positionInFeeds    feed 流中的位置
         * @param itemGroupVO        item 组 vo
         * @param nuwaItemGroupIndex 数据在原始数据中的位置
         */
        public BinderGroup(NuwaGroupRecyclerViewAdapter groupRecyclerViewAdapter, T dataVO, int positionInVOGroup,
                           int startInFeedList, int positionInFeeds, Object itemGroupVO, int nuwaItemGroupIndex) {
            mAdapter = groupRecyclerViewAdapter;
            mFeedVO = dataVO;
            mPositionInGroupList = positionInVOGroup;
            mStartPositionInFeedList = startInFeedList;
            mNuwaGroupVO = itemGroupVO;
            mNuwaItemGroupIndex = nuwaItemGroupIndex;
            mPositionInFeeds = positionInFeeds;
        }

        public boolean isItemGroup() {
            return mNuwaGroupVO instanceof INuwaGroupVO;
        }

        /**
         * 更新当前的位置信息
         */
        public void update() {
            BinderGroup binderGroup = mAdapter.getBinderGroup(mFeedVO);
            mPositionInGroupList = binderGroup.mPositionInGroupList;
            mPositionInFeeds = binderGroup.mPositionInFeeds;
            mStartPositionInFeedList = binderGroup.mStartPositionInFeedList;
            mNuwaItemGroupIndex = binderGroup.mNuwaItemGroupIndex;
        }
    }
}
